
//--Yangxin--

`include "defines.v"

module if_stage(
	input  wire           				clk  		     ,
	input  wire           				reset		     ,
	//allowin
	input  wire           				ds_allowin	     ,
	//brbus
	input  wire [      `BR_BUS_WD -1:0] br_bus           ,    // To do
	//to ds
	output wire    		  				fs_to_ds_valid   ,
	output wire [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus     ,
	//input wire [				63:  0] inst_addr        ,

	//inst sram inteface
	//output wire        				inst_sram_en     ,
	//output wire [				   7:0] inst_sram_wen    ,
	output wire 						inst_sram_wr     ,
	output wire [				  63:0] inst_sram_addr   ,
	output wire [	 			  63:0] inst_sram_wdata  ,
	//input  wire [				  31:0] inst_sram_rdata  ,
	input  wire [				  63:0] inst_sram_rdata  ,
	//add handshake
	output wire                         inst_sram_req    ,
	output wire [                  1:0] inst_sram_size 	 ,
	input  wire                         inst_sram_addr_ok,
	input  wire 						inst_sram_data_ok,

	//exception
	input  wire                         except_flush     ,
	input  wire [				  63:0]	except_new_pc   
	);

reg         inst_sram_en   ;
wire [7:0] 	inst_sram_wen  ;

assign      inst_sram_size = 2'b11;

wire 		fs_valid       ;
wire    	fs_ready_go    ;
wire    	fs_allowin     ;
wire    	to_fs_valid	   ;

reg  [63:0] seq_pc         ;
wire [63:0] next_pc        ;

wire        br_taken       ;
wire [63:0] br_target      ;

wire        pre_fs_ready_go;
wire        br_stall       ;

assign  {br_stall,br_taken,br_target} = br_bus;

wire [31:0] fs_inst        ;
wire [63:0] fs_pc          ;
assign fs_to_ds_bus = {fs_inst, //95:64
					   fs_pc    //63:0
					   };

//pre-IF stage

assign pre_fs_ready_go = ~br_stall && (inst_sram_req & inst_sram_addr_ok); //pre-IF send request
assign to_fs_valid = inst_sram_data_ok;

wire handshake   ;
assign handshake = inst_sram_data_ok;

always @(posedge clk) begin
	if(reset) begin
	  	seq_pc <= `PC_START;
	end
	else if(except_flush) begin
		seq_pc <= except_new_pc;
	end
	else if(handshake) begin
		seq_pc <= fs_pc + 4;
	end
	else begin
	  	seq_pc <= seq_pc;
	end
end

assign next_pc = (br_taken | br_taken_flag) ? br_target : seq_pc;  //for inst_addr



parameter PC_START_RESET = `PC_START - 4;
//parameter PC_START_RESET = `PC_START;

//inst_sram_req
assign inst_sram_req = inst_sram_en;

assign fs_valid = inst_sram_data_ok;

reg br_taken_flag;
always @(posedge clk) begin
	if(reset) begin
		br_taken_flag <= 1'b0;
	end
	else if(br_taken) begin
		br_taken_flag <= 1'b1;
	end
	else if(inst_sram_data_ok) begin
		br_taken_flag <= 1'b0;
	end
end

// reg except_flush_hold;
// always @(posedge clk) begin
// 	if(reset) begin
// 		except_flush_hold <= 1'b0;
// 	end
// 	else if(except_flush) begin
// 		except_flush_hold <= 1'b1;
// 	end
// 	else if(inst_sram_data_ok) begin
// 		except_flush_hold <= 1'b0;
// 	end
// end

// wire except_flush_true;
// assign except_flush_true = except_flush | except_flush_hold;
//IF stage
assign fs_ready_go = ~br_taken && inst_sram_data_ok;
//assign fs_allowin = !fs_valid || ds_allowin    ;
assign fs_allowin = !fs_valid && ds_allowin    ;
assign fs_to_ds_valid = fs_valid && fs_ready_go;
assign fs_pc = (except_flush) ? except_new_pc:
			   						  next_pc;

assign inst_sram_en    = ~reset && fs_allowin;
assign inst_sram_wen   = 8'h0;
assign inst_sram_addr  = next_pc;
assign inst_sram_wdata = 64'h0;
assign fs_inst = inst_sram_rdata[31:0];

endmodule
